OpenLayers.ProxyHost = '/cgi-bin/proxy.cgi?url=';
//OpenLayers.ProxyHost = '/geoserver/rest/proxy?url=';

// Pink tile avoidance.
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 5;
// Make OL compute scale according to WMS spec.
OpenLayers.DOTS_PER_INCH = 25.4 / 0.28;
var map;

var globLineString;
var globBounds;
var wfs_layer;

var projections = {
	map:  {srsName: 'EPSG:900913'},	// Map is in spherical mercator = Google.
	display: {srsName: 'EPSG:4326'}		// Display is in WGS84 lat/lon.
};
for (var projection in projections){
	projections[projection].olProjection = new OpenLayers.Projection(projections[projection].srsName);
};
var victoria = new OpenLayers.Bounds(
	141.0, -39.0,
	150.1, -34.0
);
victoria.transform(projections.display.olProjection, projections.map.olProjection);

var urls = {
	wms : {
		bores:	'http://maps.ubspatial.com.au/groundwater/wms?service=WMS'
	},
	wfs : {
		ffsr:	'http://siss.versi.edu.au/geoserver/wfs?service=WFS',
		ubs:	'http://maps.ubspatial.com.au:8000/geoserver/wfs?service=WFS',
		loc:	'http://localhost:8080/geoserver/wfs?service=WFS'
	}
}
var layers = {
	gmap: {},
	osgeo: {},
	wms: {},
	wfs: {
		ffsr: {},
		ubs: {}
	}
};
var wfs = {};

function init() {
	
	var mapOptions = {
		projection: projections.map.olProjection,
		displayProjection: projections.display.olProjection,
		units: "m",
		maxResolution: 156543.0339,
		maxExtent: victoria,
		//numZoomLevels: 10,
		//maxZoomLevel: 18,
		//minZoomLevel: 8,
		controls: []
	};
	map = new OpenLayers.Map('map', mapOptions);

	// Create Google layer.
	layers.gmap = new OpenLayers.Layer.Google("Google Streets", // The default.
		{numZoomLevels: 20}
	);
	
	// Create OSGeo wms layer.
	layers.osgeo = new OpenLayers.Layer.WMS("OpenLayers WMS",
		"http://vmap0.tiles.osgeo.org/wms/vmap0?", 
		{layers: 'basic'}
	);
	
	// Create groundwater bores layer.
	layers.wms.bores = new OpenLayers.Layer.WMS("Bores", 
		urls.wms.bores, 
		{
			layers: 'groundwater_bores,other_bores', 
			transparent: true,
			isBaseLayer: false, 
			singleTile: true, 
			opacity: 0.6,
			visibility: true
		}
	);
	
	//xmlns:gsml="urn:cgi:xmlns:CGI:GeoSciML:2.0"
	//xmlns:gwml="http://www.nrcan.gc.ca/xml/gwml/1"
	
	// Create VeRSI SISS FFSR WFS layers.
	layers.wfs.ffsr.borehole = new OpenLayers.Layer.Vector("FFSR WFS", {
		strategies: [new OpenLayers.Strategy.BBOX()],
		protocol: new OpenLayers.Protocol.WFS({
			url: urls.wfs.ffsr,
			featureType: 'Borehole',
			//geometryName: 'collarLocation',
			//geometryName: 'BoreholeCollar',
			//geometryName: 'location',
			//geometryName: 'Point',
			//featureNS: "http://www.openplans.org/topp",
			extractAttributes : true,
			maxFeatures: 1000,
			srsName: projections.map.srsName,
			version: '1.1.0'
		})
	});        
	layers.wfs.ffsr.waterwell = new OpenLayers.Layer.Vector("FFSR WFS", {
		strategies: [new OpenLayers.Strategy.BBOX()],
		protocol: new OpenLayers.Protocol.WFS({
			url: urls.wfs.ffsr,
			featureType: 'WaterWell',
			//geometryName: 'sa:position',
			featurePrefix: 'gwml',
			featureNS: "http://www.nrcan.gc.ca/xml/gwml/1",
			extractAttributes : true,
			maxFeatures: 10,
			srsName: projections.map.srsName,
			version: '1.1.0'
		})
	});        

	// Create UBSpatial SISS WFS layers.
	layers.wfs.ubs.borehole = new OpenLayers.Layer.Vector("UBS WFS", {
		strategies: [new OpenLayers.Strategy.BBOX()],
		protocol: new OpenLayers.Protocol.WFS({
			url: urls.wfs.ubs,
			featureType: 'Borehole',
			//geometryName: 'collarLocation',
			//geometryName: 'BoreholeCollar',
			//geometryName: 'location',
			//geometryName: 'Point',
			//featureNS: "http://www.openplans.org/topp",
			extractAttributes : true,
			maxFeatures: 100,
			srsName: projections.map.srsName,
			version: '1.1.0'
		})
	});        
	layers.wfs.ubs.waterwell = new OpenLayers.Layer.Vector("UBS WFS", {
		strategies: [new OpenLayers.Strategy.BBOX()],
		protocol: new OpenLayers.Protocol.WFS({
			url: urls.wfs.ubs,
			featureType: 'WaterWell',
			//geometryName: 'collarLocation',
			//geometryName: 'BoreholeCollar',
			//geometryName: 'location',
			//geometryName: 'Point',
			//featureNS: "http://www.openplans.org/topp",
			extractAttributes : true,
			maxFeatures: 10,
			srsName: projections.map.srsName,
			version: '1.1.0'
		})
	});        

	// Add layers to map.
	//map.addLayer(layers.gmap);
	map.addLayer(layers.osgeo);
	map.addLayer(layers.wms.bores);
	wfs = layers.wfs.ffsr.borehole;
	//wfs = layers.wfs.ffsr.waterwell;
	//wfs = layers.wfs.ubs.borehole;
	//wfs = layers.wfs.ubs.waterwell;
	//map.addLayer(wfs);

	// Build up all controls.
	map.addControl(new OpenLayers.Control.PanZoomBar({
		position: new OpenLayers.Pixel(2, 15)
	}));
	map.addControl(new OpenLayers.Control.Navigation());
	map.addControl(new OpenLayers.Control.Scale($('scale')));
	map.addControl(new OpenLayers.Control.MousePosition({element: $('location')}));
	
	map.zoomToExtent(victoria);
	
	// Support GetFeatureInfo
/*			
	map.events.register('click', map, function (e) {
		document.getElementById('nodelist').innerHTML = "Loading... please wait...";
		var params = {
			REQUEST: "GetFeatureInfo",
			EXCEPTIONS: "application/vnd.ogc.se_xml",
			BBOX: map.getExtent().toBBOX(),
			X: e.xy.x,
			Y: e.xy.y,
			INFO_FORMAT: 'text/html',
			QUERY_LAYERS: map.layers[0].params.LAYERS,
			FEATURE_COUNT: 50,
			Layers: 'medford',
			Styles: '',
			Srs: 'EPSG:4326',
			WIDTH: map.size.w,
			HEIGHT: map.size.h,
			format: format};
		OpenLayers.loadURL("http://localhost:8080/geoserver/wms", params, this, setHTML, setHTML);
		OpenLayers.Event.stop(e);
	});
*/			
}

// Sets the HTML provided into the nodelist element.
function setHTML(response) {
	document.getElementById('nodelist').innerHTML = response.responseText;
}


function showFeature(feature) {             
	var msg = "Geom: ";
	if (feature.geometry) {
		msg += feature.geometry.toString();
	}
	msg += "\r\n";
	for (var key in feature.attributes){
		msg += key + ":" + feature.attributes[key] + "\r\n";
	};
	alert(msg);
};
function showAllFeatures() {
	alert('Features: '+wfs.features.length);
	for (var i in wfs.features){
		showFeature(wfs.features[i]);
	};
};

function getRaw() {
	document.getElementById('nodelist').innerHTML = "Loading... please wait...";
	var params = {
		request: "GetFeature",
		typeName: "gwml:WaterWell",
		maxFeatures: 50,
		srsName: projections.map.srsName,
		//outputFormat: 'json',
		//EXCEPTIONS: "application/vnd.ogc.se_xml",
		//BBOX: map.getExtent().toBBOX(),
		//X: e.xy.x,
		//Y: e.xy.y,
		//QUERY_LAYERS: map.layers[0].params.LAYERS,
		Version: '1.1.0'
	};
	// try wfslayer.getFullRequestString
	OpenLayers.loadURL(urls.wfs.ffsr, params, this, setHTML, setHTML);
}
function getBox() {
	//document.getElementById('nodelist').innerHTML = "Loading... please wait...";

	var bounds1 = new OpenLayers.Bounds(144.0882, -37.77, 145.2, -36.65);
	var bbox1_filter = new OpenLayers.Filter.Spatial({
		type: OpenLayers.Filter.Spatial.BBOX,
		value: bounds1,
		projection: projections.display.srsName
	});
	
	var points = [];
	//points.push(new OpenLayers.Geometry.Point(144.26, -37.23));
	//points.push(new OpenLayers.Geometry.Point(144.70, -36.93));
	points.push(new OpenLayers.Geometry.Point(143.419, -38.419));
	points.push(new OpenLayers.Geometry.Point(143.820, -38.285));
	var lineString = new OpenLayers.Geometry.LineString(points);

	var bounds2 = new OpenLayers.Bounds();
	for (i in points) {bounds2.extend(points[i]);};
	var bbox2_filter = new OpenLayers.Filter.Spatial({
		type: OpenLayers.Filter.Spatial.BBOX,
		value: bounds2,
		projection: projections.display.srsName
	});
	var bounds2b = bounds2.clone();
	bounds2b.transform(projections.display.olProjection, projections.map.olProjection);
	var bbox2b_filter = new OpenLayers.Filter.Spatial({
		type: OpenLayers.Filter.Spatial.BBOX,
		value: bounds2b,
		projection: projections.map.srsName
	});

	var metresPerDegree = 112500.0;  // Approximate number of metres per longitude degree in Victoria
	lineString.transform(projections.display.olProjection, projections.map.olProjection);
	globLineString = lineString;
	var dist_filter = new OpenLayers.Filter.Spatial({
		type: OpenLayers.Filter.Spatial.DWITHIN,
		distanceUnits: 'm',
		distance: 1000.0/metresPerDegree,
		//distance: 0.05,  // This seems to be in units of the underlying data, regardless of any applied transformation, or distanceUnits.
		//distance: 1000,
		projection: projections.map.srsName,
		//projection: projections.display.srsName,
		value: lineString
	});

	wfs_layer = new OpenLayers.Layer.Vector("FFSR WFS", {
		strategies: [
			//new OpenLayers.Strategy.Filter({filter: dist_filter}),
			//new OpenLayers.Strategy.Filter({filter: bbox2b_filter}),
			new OpenLayers.Strategy.BBOX()
		],
		protocol: new OpenLayers.Protocol.WFS({
			url: urls.wfs.ffsr,
			//url: urls.wfs.ubs,
			//url: urls.wfs.loc,
			//featureType: 'Borehole', // For ffsr and ubs
			featureType: 'dbo_sites', // For ffsr
			//featureType: 'GMS_site_summary_loc',  // For loc
			extractAttributes : true,
			maxFeatures: 1000,
			srsName: projections.map.srsName,
			version: '1.1.0'
		})
		, filter: dist_filter
		//, filter: bbox2b_filter
	});
	map.addLayer(wfs_layer);
	map.zoomToExtent(bounds2b);
	
	//The following doesn't work as the load is asynchronous.
	//document.getElementById('nodelist').innerHTML = "Loaded "+wfs_layer.features.length;
}
function getRecords(layer, line) {
	/* 
	Return an array of data records, derived from all the features in 'layer',
	with respect to the perpendicular distance to 'line', where
	'line' is truncated to two points running from west to east.
	The records contain: 
		attributes: the original features.attributes,
		point: the Point geometry of the feature,
		ID: an ID of the feature or null,
		M: the linear distance of the feature measured along the line from the first point of the line,
		offset: the offset of the feature from the line, measured perpendicularly
	The coordinates of features and of the line must be in the same projection, no transformation is performed.
	M and offset are in units of that projection.
	*/
	function pointAngle(p1, p2) {return Math.atan2(p2.y-p1.y, p2.x-p1.x);}
	function lineStringToLine (lineString) {
		/* 
		Only use the first two points of the lineString 
		and make it run from west to east (i.e. make [0].x<=[1].x).
		*/
		var point;
		if (lineString.components && lineString.components.length>=2) {
			while (lineString.components.length>2) {
				lineString.pop();
			}
			if (lineString.components[0].x>lineString.components[1].x) {
				point = lineString.components[0].clone();
				lineString.components[0] = lineString.components[1].clone();
				lineString.components[1] = point;
			}
		} else {
			lineString = [];
		}
	}

	var records = [];
	var lineLength = 0.0;
	var lineAngle = 0.0;
	var i;
	var feature;
	var featurePoint;
	var featureDist = 0.0;
	var featureAngle = 0.0;
	var featureAngleToLine = 0.0;
	
	lineStringToLine(line);
	if (layer && line) {
		lineLength = line.getLength();
		lineAngle = pointAngle(line.components[0],line.components[1]);
		for (i in layer.features) {
			feature = layer.features[i];
			featurePoint = feature.geometry;  // Assumed to be a point!
			featureDist = featurePoint.distanceTo(line.components[0]);
			featureAngle = pointAngle(line.components[0], featurePoint);
			featureAngleToLine = featureAngle-lineAngle;
			records.push({
				ID: i,
				M: featureDist*Math.cos(featureAngleToLine),
				offset: featureDist*Math.sin(featureAngleToLine),  // Positive is to the left of the line (looking from 0 to 1), negative is to the right of the line.
				point: featurePoint,
				attributes: feature.attributes
			});
		}
	}
	return records;
	
}
function getFeatureConnectionLayer(records, line) {
	var layer;
	var startPoint;
	var dx = 0.0, dy =0.0, len = 0.0;
	var lineStringFeatures = [];
	var i;
	var ratio = 0.0;
	var basePoint;
	var lineString;
	
	layer = new OpenLayers.Layer.Vector("Distance lines",{
		projection: projections.map.olProjection,
		isBaseLayer: false,
		visibility: true
	});
	layer.addFeatures([new OpenLayers.Feature.Vector(line)]);  // Add the selection line.
	startPoint = line.components[0];
	dx = line.components[1].x-startPoint.x;
	dy = line.components[1].y-startPoint.y;
	len = startPoint.distanceTo(line.components[1]);
	for (i in records) {
		ratio = records[i].M/len;
		if (0.0<=ratio && ratio<=1.0) {
			basePoint = new OpenLayers.Geometry.Point(startPoint.x+ratio*dx, startPoint.y+ratio*dy);
			lineString = new OpenLayers.Geometry.LineString([basePoint, records[i].point]);
			lineStringFeatures.push(new OpenLayers.Feature.Vector(lineString));
		}
	}
	layer.addFeatures(lineStringFeatures);
	return layer;
}
function showFeatures() {
	var records = getRecords(wfs_layer, globLineString);
	alert(records.length);
	var layer = getFeatureConnectionLayer(records, globLineString);
	map.addLayer(layer);
	for (var i in records) {
		//alert(records[i].dist);
	}
}
